package org.redswallow.filter.plugin;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.redswallow.filter.Constants;
import org.redswallow.log.LogFactory;
import org.redswallow.util.Utils;
import org.redswallow.util.config.ConfigConstants;
import org.redswallow.util.config.IPropertySet;
import org.redswallow.util.config.MappedPropertySet;
import org.slf4j.Logger;

public class HtmlEscapeCharPlugin extends AbstractParameterCheckPlugin implements Plugin {
	
	public static final String MODULE_NAME = "HTML Escape Character Plugin";
    public static final String MODULE_VERSION = "v1.0";
    public static final String MODULE_DESCRIPTION = "Change special character to safe word.";
    
	
    private static final String CONFIG_CHECK_PARAMNAME = "checkname";
    private static final String CONFIG_ESC = "escape";
    
    private static final String CONFIG_INCLUDE_PATHPATTENS = "includePathPatterns";
    private static final String CONFIG_EXCLUDE_PATHPATTENS = "excludePathPatterns";
    
    
	private static Map<String,String> escapes = new LinkedHashMap<String,String>();
	
	//是否检查参数名称的规范化,默认不检查
	private boolean checkName;
	
	
	//对指定路径进行检查或者不检查
	private boolean includePathPatterns;
	
	//指定的路径规则表达式
	private Collection<Pattern> pathPatterns;
	
	static {
		escapes.put("&", "&amp;");
		escapes.put("<", "&lt;");
		escapes.put(">", "&gt;");
	}
	
	private Logger log = LogFactory.getLog(HtmlEscapeCharPlugin.class);
	
	public HtmlEscapeCharPlugin(IPropertySet config) {
		
		super(config);
		
		loadEscapesRule(config);
		
		parseExtendParams(config);
	}
	
	private void loadEscapesRule(IPropertySet config) {
		IPropertySet escConfig = new MappedPropertySet(CONFIG_ESC, config);
		int count = escConfig.getInteger(ConfigConstants.CONFIG_COUNT, 0);
		for(int i=0; i<count; ++i) {
			String param = escConfig.getString(""+(i+1), null);
			if (Utils.isNonBlank(param)) {
				String[] rule = param.split(",");
				if (rule.length == 2) {
					escapes.put(rule[0], rule[1]);
					log.debug("load escape rule ['{}','{}']", rule);
				}
			}
		}
	}
	
	private void parseExtendParams(IPropertySet config) {
		
		//是否检查参数名称
		checkName = config.getBoolean(CONFIG_CHECK_PARAMNAME, false);
		
		//检查有无指定的路径
		String includePathPatternsString = config.getString(CONFIG_INCLUDE_PATHPATTENS, null);
		String excludePathPatternsString = config.getString(CONFIG_EXCLUDE_PATHPATTENS, null);
		
		if (Utils.isNonBlank(includePathPatternsString) && Utils.isNonBlank(excludePathPatternsString) ) {
			throw new IllegalArgumentException("Can't specify both "+CONFIG_INCLUDE_PATHPATTENS+" and "+CONFIG_EXCLUDE_PATHPATTENS);
		}
		
		if (Utils.isBlank(includePathPatternsString)) {
			includePathPatterns = false;
			pathPatterns = parsePatterns(excludePathPatternsString);
		}else {
			includePathPatterns = true;
			pathPatterns = parsePatterns(includePathPatternsString);
		}
		
	}
	
	private boolean isTargetPath(String path) {
		if (path != null) {
			for (Pattern pattern : pathPatterns) {
				if (pattern.matcher(path).matches()) {
					return includePathPatterns;
				}
			}
		}
		return !includePathPatterns;
	}
	

	@Override
	public String getDescription() {
		return MODULE_DESCRIPTION;
	}

	@Override
	public String getName() {
		return MODULE_NAME;
	}

	@Override
	public String getVendor() {
		return Constants.VENDOR;
	}

	@Override
	public String getVersion() {
		return MODULE_VERSION;
	}

	@Override
	public void initialize() {
	}
	
	
	
	@Override
	public boolean invoke(ServletRequest request, ServletResponse response) {
		
		//检查URL是否需要处理
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String uri = httpRequest.getRequestURI();
		
		if (!isTargetPath(uri)) {
			log.debug("Path '{}' isnot target path.");
			return true;
		}
		
		return super.invoke(request, response);
	}
	
	@Override
	public void shutdown() {
		// TODO Auto-generated method stub
		
	}

	@Override
	protected boolean checkParam(String name, Map<String, String[]> map,
			ServletResponse response) {
		
		Boolean find;
		
		String[] values = map.get(name);
		
		if (checkName) {
			String newName = name;
			find = false;
			for(String regex : escapes.keySet()) {
				if (Pattern.compile(regex).matcher(name).find()) {
					newName = newName.replaceAll(regex, escapes.get(regex));
					find = true;
				}
			}
			if (find) {
				map.remove(name);
				map.put(newName, values);
				
				log.debug("param name change {} to {}.", new Object[]{name, newName});
			}
		}
		
		for(int i=0; i<values.length; ++i) {
			String value = values[i];
			find = false;
			for(String regex : escapes.keySet()) {
				if (Pattern.compile(regex).matcher(value).find()) {
					value = value.replaceAll(regex, escapes.get(regex));
					find = true;
				}
			}
			if (find) {
				log.debug("param {} values change {} to {}.", new Object[]{name, values[i], value});
				values[i] =  value;
			}
		}
		
		return true;
	}
	
}
